Objavte pokročilé typy TypeScriptu: literálové šablóny a podmienkové typy. Píšte expresívnejší a udržiavateľnejší kód. Zvládnite komplexnú manipuláciu s typmi.
Pokročilé typy v TypeScripte: Ovládanie typov s literálmi šablón a podmienkových typov
Sila TypeScriptu spočíva v jeho robustnom typovom systéme. Zatiaľ čo základné typy ako string, number a boolean sú dostatočné pre mnohé scenáre, pokročilé funkcie ako typy s literálmi šablón a podmienkové typy odomykajú novú úroveň expresivity a typovej bezpečnosti. Tento sprievodca poskytuje komplexný prehľad týchto pokročilých typov, skúma ich možnosti a demonštruje praktické aplikácie.
Pochopenie typov s literálmi šablón
Typy s literálmi šablón vychádzajú z literálov šablón v Javascripte, čo vám umožňuje definovať typy na základe reťazcovej interpolácie. To umožňuje vytváranie typov, ktoré reprezentujú špecifické reťazcové vzory, čím sa váš kód stáva robustnejším a predvídateľnejším.
Základná syntax a použitie
Typy s literálmi šablón používajú spätné apostrofy (`) na ohraničenie definície typu, podobne ako literály šablón v Javascripte. V rámci spätných apostrofov môžete interpolovať iné typy pomocou syntaxe ${}. Tu sa deje kúzlo – v podstate vytvárate typ, ktorý je reťazcom, konštruovaným v čase kompilácie na základe typov vo vnútri interpolácie.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = \`/api/${string}\`;
// Príklad použitia
const getEndpoint: APIEndpoint = "/api/users"; // Platné
const postEndpoint: APIEndpoint = "/api/products/123"; // Platné
const invalidEndpoint: APIEndpoint = "/admin/settings"; // TypeScript tu neukáže chybu, pretože \`string\` môže byť čokoľvek
V tomto príklade je APIEndpoint typ, ktorý reprezentuje akýkoľvek reťazec začínajúci /api/. Hoci tento základný príklad je užitočný, skutočná sila typov s literálmi šablón sa prejaví v kombinácii so špecifickejšími typovými obmedzeniami.
Kombinovanie s union typmi
Typy s literálmi šablón skutočne vyniknú, keď sa použijú s union typmi. To vám umožňuje vytvárať typy, ktoré reprezentujú špecifickú množinu reťazcových kombinácií.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIPath = "users" | "products" | "orders";
type APIEndpoint = \`/${APIPath}/${HTTPMethod}\`;
// Platné API koncové body
const getUsers: APIEndpoint = "/users/GET";
const postProducts: APIEndpoint = "/products/POST";
// Neplatné API koncové body (spôsobí chyby TypeScriptu)
// const invalidEndpoint: APIEndpoint = "/users/PATCH"; // Error: "/users/PATCH" is not assignable to type "/users/GET" | "/users/POST" | "/users/PUT" | "/users/DELETE" | "/products/GET" | "/products/POST" | ... 3 more ... | "/orders/DELETE".
Teraz je APIEndpoint reštriktívnejší typ, ktorý povoľuje len špecifické kombinácie API ciest a HTTP metód. TypeScript označí akékoľvek pokusy o použitie neplatných kombinácií, čím sa zvyšuje typová bezpečnosť.
Manipulácia s reťazcami pomocou typov s literálmi šablón
TypeScript poskytuje vnútorné typy na manipuláciu s reťazcami, ktoré bezproblémovo fungujú s typmi s literálmi šablón. Tieto typy vám umožňujú transformovať reťazce v čase kompilácie.
- Uppercase: Konvertuje reťazec na veľké písmená.
- Lowercase: Konvertuje reťazec na malé písmená.
- Capitalize: Kapitalizuje prvé písmeno reťazca.
- Uncapitalize: Odstraňuje kapitalizáciu prvého písmena reťazca.
type Greeting = "hello world";
type UppercaseGreeting = Uppercase; // "HELLO WORLD"
type LowercaseGreeting = Lowercase; // "hello world"
type CapitalizedGreeting = Capitalize; // "Hello world"
type UncapitalizedGreeting = Uncapitalize; // "hello world"
Tieto typy na manipuláciu s reťazcami sú obzvlášť užitočné na automatické generovanie typov na základe konvencií pomenovania. Napríklad môžete odvodiť typy akcií z názvov udalostí alebo naopak.
Praktické aplikácie typov s literálmi šablón
- Definícia API koncových bodov: Ako bolo demonštrované vyššie, definovanie API koncových bodov s presnými typovými obmedzeniami.
- Správa udalostí: Vytváranie typov pre názvy udalostí so špecifickými predponami a príponami.
- Generovanie CSS tried: Generovanie názvov CSS tried na základe názvov komponentov a stavov.
- Tvorba databázových dopytov: Zabezpečenie typovej bezpečnosti pri konštrukcii databázových dopytov.
Medzinárodný príklad: Formátovanie meny
Predstavte si, že vytvárate finančnú aplikáciu, ktorá podporuje viacero mien. Typy s literálmi šablón môžete použiť na vynútenie správneho formátovania meny.
type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";
type CurrencyFormat = \`${number} ${T}\`;
const priceUSD: CurrencyFormat<"USD"> = "100 USD"; // Platné
const priceEUR: CurrencyFormat<"EUR"> = "50 EUR"; // Platné
// const priceInvalid: CurrencyFormat<"USD"> = "100 EUR"; // Error: Type 'string' is not assignable to type '\`${number} USD`'.
function formatCurrency(amount: number, currency: T): CurrencyFormat {
return \`${amount} ${currency}\`;
}
const formattedUSD = formatCurrency(250, "USD"); // Type: "250 USD"
const formattedEUR = formatCurrency(100, "EUR"); // Type: "100 EUR"
Tento príklad zaisťuje, že hodnoty meny sú vždy formátované so správnym kódom meny, čím sa predchádza potenciálnym chybám.
Ponorenie sa do podmienkových typov
Podmienkové typy zavádzajú vetviacu logiku do typového systému TypeScriptu, čo vám umožňuje definovať typy, ktoré závisia od iných typov. Táto funkcia je neuveriteľne silná pre vytváranie vysoko flexibilných a opakovane použiteľných definícií typov.
Základná syntax a použitie
Podmienkové typy používajú kľúčové slovo infer a ternárny operátor (condition ? trueType : falseType) na definovanie typových podmienok.
type IsString = T extends string ? true : false;
type StringCheck = IsString; // type StringCheck = true
type NumberCheck = IsString; // type NumberCheck = false
V tomto príklade je IsString podmienkový typ, ktorý kontroluje, či je T priraditeľný k string. Ak áno, typ sa vyhodnotí ako true; inak sa vyhodnotí ako false.
Kľúčové slovo infer
Kľúčové slovo infer vám umožňuje extrahovať typ z iného typu. To je obzvlášť užitočné pri práci s komplexnými typmi, ako sú funkčné typy alebo typy polí.
type ReturnType any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType; // type AddReturnType = number
V tomto príklade ReturnType extrahuje návratový typ funkčného typu T. Časť infer R podmienkového typu odvodí návratový typ a priradí ho typovej premennej R. Ak T nie je funkčný typ, typ sa vyhodnotí ako any.
Distributívne podmienkové typy
Podmienkové typy sa stávajú distributívnymi, keď je kontrolovaný typ holým typovým parametrom. To znamená, že podmienkový typ sa aplikuje na každého člena union typu samostatne.
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray; // type NumberOrStringArray = string[] | number[]
V tomto príklade ToArray konvertuje typ T na typ poľa. Pretože T je holý typový parameter (nie je zabalený v inom type), podmienkový typ sa aplikuje na number a string samostatne, čo vedie k unionu number[] a string[].
Praktické aplikácie podmienkových typov
- Extrahovanie návratových typov: Ako bolo demonštrované vyššie, extrahovanie návratového typu funkcie.
- Filtrovanie typov z unionu: Vytváranie typu, ktorý obsahuje iba špecifické typy z unionu.
- Definovanie preťažených funkčných typov: Vytváranie rôznych funkčných typov na základe vstupných typov.
- Vytváranie typových stráží: Definovanie funkcií, ktoré zužujú typ premennej.
Medzinárodný príklad: Spracovanie rôznych formátov dátumu
Rôzne regióny sveta používajú rôzne formáty dátumu. Podmienkové typy môžete použiť na spracovanie týchto variácií.
type DateFormat = "YYYY-MM-DD" | "MM/DD/YYYY" | "DD.MM.YYYY";
type ParseDate = T extends "YYYY-MM-DD"
? { year: number; month: number; day: number; format: "YYYY-MM-DD" }
: T extends "MM/DD/YYYY"
? { month: number; day: number; year: number; format: "MM/DD/YYYY" }
: T extends "DD.MM.YYYY"
? { day: number; month: number; year: number; format: "DD.MM.YYYY" }
: never;
function parseDate(dateString: string, format: T): ParseDate {
// (Implementácia by spracovala rôzne formáty dátumu)
if (format === "YYYY-MM-DD") {
const [year, month, day] = dateString.split("-").map(Number);
return { year, month, day, format } as ParseDate;
} else if (format === "MM/DD/YYYY") {
const [month, day, year] = dateString.split("/").map(Number);
return { month, day, year, format } as ParseDate;
} else if (format === "DD.MM.YYYY") {
const [day, month, year] = dateString.split(".").map(Number);
return { day, month, year, format } as ParseDate;
} else {
throw new Error("Invalid date format");
}
}
const parsedDateISO = parseDate("2023-10-27", "YYYY-MM-DD"); // Type: { year: number; month: number; day: number; format: "YYYY-MM-DD"; }
const parsedDateUS = parseDate("10/27/2023", "MM/DD/YYYY"); // Type: { month: number; day: number; year: number; format: "MM/DD/YYYY"; }
const parsedDateEU = parseDate("27.10.2023", "DD.MM.YYYY"); // Type: { day: number; month: number; year: number; format: "DD.MM.YYYY"; }
console.log(parsedDateISO.year); // Prístup k roku s vedomím, že tam bude
Tento príklad používa podmienkové typy na definovanie rôznych funkcií parsovania dátumu na základe určeného formátu dátumu. Typ ParseDate zaisťuje, že vrátený objekt má správne vlastnosti na základe formátu.
Kombinovanie typov s literálmi šablón a podmienkových typov
type EventName = \`on${Capitalize}\`;
type ExtractEventPayload = T extends EventName
? { type: T; payload: any } // Zjednodušené pre demonštráciu
: never;
type ClickEvent = EventName<"click">; // "onClick"
type MouseOverEvent = EventName<"mouseOver">; // "onMouseOver"
//Príklad funkcie, ktorá prijíma typ
function processEvent(event: T): ExtractEventPayload {
//V skutočnej implementácii by sme udalosť skutočne odoslali.
console.log(\`Processing event ${event}\`);
//V skutočnej implementácii by sa payload (dáta) zakladal na type udalosti.
return { type: event, payload: {} } as ExtractEventPayload;
}
//Všimnite si, že návratové typy sú veľmi špecifické:
const clickEvent = processEvent("onClick"); // { type: "onClick"; payload: any; }
const mouseOverEvent = processEvent("onMouseOver"); // { type: "onMouseOver"; payload: any; }
//Ak použijete iné reťazce, dostanete never:
// const someOtherEvent = processEvent("someOtherEvent"); // Type is \`never\`
Osvedčené postupy a úvahy
- Zachovajte jednoduchosť: Hoci sú tieto pokročilé typy výkonné, môžu sa rýchlo stať komplexnými. Usilujte sa o jasnosť a udržiavateľnosť.
- Dôkladne testujte: Zabezpečte, aby sa vaše definície typov správali podľa očakávania, písaním komplexných unit testov.
- Dokumentujte svoj kód: Jasne dokumentujte účel a správanie vašich pokročilých typov, aby ste zlepšili čitateľnosť kódu.
- Zvážte výkon: Nadmerné používanie pokročilých typov môže ovplyvniť čas kompilácie. Profilujte svoj kód a optimalizujte ho tam, kde je to potrebné.
Záver
Typy s literálmi šablón a podmienkové typy sú výkonné nástroje v arzenáli TypeScriptu. Ovládaním týchto pokročilých typov môžete písať expresívnejší, udržiavateľnejší a typovo bezpečný kód. Tieto funkcie vám umožňujú zachytiť komplexné vzťahy medzi typmi, vynútiť prísnejšie obmedzenia a vytvárať vysoko opakovateľne použiteľné definície typov. Osvojte si tieto techniky, aby ste pozdvihli svoje svoje zručnosti v TypeScripte a vytvárali robustné a škálovateľné aplikácie pre globálne publikum.